package com.wuhaha.libnetwork2.network;

import android.annotation.SuppressLint;
import android.text.TextUtils;
import android.util.Log;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.arch.core.executor.ArchTaskExecutor;

import com.wuhaha.libnetwork2.cache.CacheManage;

import org.jetbrains.annotations.NotNull;

import java.io.IOException;
import java.io.Serializable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.Response;

public abstract class Request<T, R extends Request> implements Cloneable {
    protected String mUrl;
    protected HashMap<String, String> headers = new HashMap<>();
    //对参数的一个存储
    protected HashMap<String, Object> params = new HashMap<>();

    //仅仅只访问本地缓存，即便本地缓存不存在，也不会发起网络请求
    public static final int CACHE_OULY = 1;
    //先访问缓存，同时并发网络请求，成功后缓存到本地
    public static final int CAHE_FIRST = 2;
    //只访问服务器，不做任何存储
    public static final int NET_OULY = 3;
    //先访问网络，成功后缓存到本地
    public static final int NEW_CACHE = 4;
    private String cacheKey;
    private Type mType;
    private Class claz;
    private int cachStrategy = NET_OULY;

    //管理类型
    @IntDef({CACHE_OULY, CAHE_FIRST, NET_OULY, NEW_CACHE})
    @Retention(RetentionPolicy.SOURCE)
    public @interface CacheStrategy {

    }

    public Request(String url) {
        //服务器域名
        mUrl = url;
    }

    //添加请求头
    public R addHeader(String key, String value) {
        headers.put(key, value);
        return (R) this;
    }

    public R addParam(String key, Object value) {
        if (value == null){
            return (R) this;
        }
        try {
            if (value.getClass() == String.class){
                params.put(key,value);
            }else{
                Field type = value.getClass().getField("TYPE");
                Class claz = (Class) type.get(null);
                //判断是否为基础数据类型
                if (claz.isPrimitive()) {
                    params.put(key, value);
                }
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return (R) this;
    }

    public R cacheStrategy(@CacheStrategy int cachStrategy){
        this.cachStrategy = cachStrategy;
        return (R)this;
    }

    public R cacheKey(String key) {
        this.cacheKey = key;
        return (R)this;
    }

    public R responseType(Type type){
        mType = type;
        return (R)this;
    }

    public R responseType(Class claz){
        this.claz = claz;
        return (R)this;
    }

    //如果传入的就事异步的，不传就是同步
    @SuppressLint("RestrictedApi")
    public void execute(JsonCallback<T> callBack){


        if (cachStrategy != NET_OULY){
            //子线程
            ArchTaskExecutor.getIOThreadExecutor().execute(new Runnable() {
                @Override
                public void run() {
                    ApiResponse<T> response = readCache();
                    Log.e("onCacheSuccess", "onCacheSuccess: "  +response.body);
                    if (callBack != null && response.body != null){
                        callBack.onCacheSuccess(response);
                    }
                }
            });
        }

        if (cachStrategy != CACHE_OULY){
            System.out.println("========================");
            getCall().enqueue(new Callback() {
                @Override
                public void onFailure(@NotNull Call call, @NotNull IOException e) {
                    ApiResponse<T> response = new ApiResponse<>();
                    response.message = e.getMessage();
                    callBack.onError(response);
                }
                //asdasdasd
                @Override
                public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {


                    ApiResponse<T> apiResponse = parseResponse(response,callBack);
                    Log.i("=======", "onResponse: "+apiResponse.message);
                    if (!apiResponse.success){
                        callBack.onError(apiResponse);
                    }else{
                        callBack.onSuccess(apiResponse);
                    }
                }
            });
        }
    }

    private ApiResponse<T> readCache(){
        String key = TextUtils.isEmpty(cacheKey) ? generaterCacheKey():cacheKey;
        Object cache = CacheManage.getCache(key);

        ApiResponse<T> resuilt = new ApiResponse<>();
        resuilt.status = 304;
        resuilt.message = "缓存获取成功";
        resuilt.body = (T) cache;
        resuilt.success = true;
        return resuilt;
    }


    private ApiResponse<T> parseResponse(Response response,JsonCallback<T> callBack){
        String message = null;
        int status = response.code();
        boolean success = response.isSuccessful();
        ApiResponse<T> result = new ApiResponse<>();
        Convert convert = ApiServices.sConvert;
        try{
            String content = response.body().string();
            if (success){
                if (callBack != null){
                    ParameterizedType type = (ParameterizedType) callBack.getClass().getGenericSuperclass();
                    Type argument = type.getActualTypeArguments()[0];
                    result.body = (T)convert.convert(content,argument);
                }else if (mType != null){
                    result.body = (T)convert.convert(content,mType);
                }
//                else if (claz != null){
//                    result.body = (T)convert.convert(content,claz);
//                }
                else{
                    Log.e("request", "parseResponse: 无法解析" );
                }
            }else{
                message = content;
            }
        }catch (Exception e){
            message = e.getMessage();
            success = false;
            status = 0;
        }

        result.success = success;
        result.status = status;
        result.message = message;

        if (cachStrategy != NET_OULY && result.success && result.body != null && result.body instanceof Serializable){
            saveCache(result.body);
        }
        return result;
    }

    private void saveCache(T body){
        String key = TextUtils.isEmpty(cacheKey)?generaterCacheKey():cacheKey;
        CacheManage.save(key,body);
    }

    public String generaterCacheKey(){
        cacheKey = UrlCreact.createUrlFromParams(mUrl,params);
        return cacheKey;
    }


    private Call getCall(){
        okhttp3.Request.Builder builder = new okhttp3.Request.Builder();
        addHeader(builder);
        okhttp3.Request request = generaterRequest(builder);
        Call call = ApiServices.build.newCall(request);
        return call;
    }

    protected abstract okhttp3.Request generaterRequest(okhttp3.Request.Builder builder);

    private void addHeader(okhttp3.Request.Builder builder){
        for (Map.Entry<String,String> entry : headers.entrySet()){
            builder.addHeader(entry.getKey(),entry.getValue());
        }
    }



    //同步
    public ApiResponse<T> execute(){
        if (mType == null){
            throw new RuntimeException("同步方法,response 返回值 类型必须设置");
        }

        if (cachStrategy == CACHE_OULY){
            return readCache();
        }

        if (cachStrategy != CACHE_OULY){
            ApiResponse<T> result = null;
            try {
                Response response = getCall().execute();
                Log.i("--temp", "execute: "+response.toString());
                result = parseResponse(response, null);
            } catch (IOException e) {
                e.printStackTrace();
                if (result == null){
                    result = new ApiResponse<>();
                    result.message = e.getMessage();
                }
            }
            return result;
        }
        return null;
    }

    @NonNull
    @NotNull
    @Override
    public Request clone() throws CloneNotSupportedException {
        return (Request<T,R>) super.clone();
    }
}
